﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Text;
using Microsoft.TeamFoundation.Client;

public class TfsWorkItems
{
    private WorkItemStore _workItemStore;
    private TswaClientHyperlinkService _hyperlinkService;

    public TfsWorkItems(TfsCollection tpc)
    {
        _workItemStore = tpc.WorkItemStore;
        _hyperlinkService = tpc.HyperlinkService;
    }

    public WorkItemInfo[] GetWorkItemTree(string projectName, IEnumerable<WorkItemConfig> workItemTypes, string whereClause, string blockedField)
    {
        // Add the parameters for the WIQL
        Dictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("project", projectName);
        parameters.Add("closedState", "Done");

        string selectWiql = @"
            SELECT	[System.Id],
                    [System.Title], 
                    [System.State], 
                    [System.AssignedTo], 
                    [System.WorkItemType],
                    [System.AreaPath],
                    [Microsoft.VSTS.Common.StackRank],
                    [Microsoft.VSTS.Common.Priority],
                    [Microsoft.VSTS.Scheduling.RemainingWork]";

        if (string.IsNullOrWhiteSpace(blockedField) == false)
        {
            selectWiql += "[" + blockedField + "]";
        }

        string wiql = selectWiql + @"
            FROM WorkItemLinks 
            WHERE (
                    Source.[System.TeamProject] = @project 
                AND Source.[System.WorkItemType] IN ({0})
                {1}
                AND (
                    Source.[System.State] <> 'Removed' 
                    AND Source.[System.State] <> @closedState
                )
            ) AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'
                AND Target.[System.WorkItemType] <> ''
            ORDER BY [Microsoft.VSTS.Common.StackRank], [Microsoft.VSTS.Common.Priority], [System.Id] mode(Recursive)
        ";

        //
        // Replace {0} with the single-quoted list of work item type names
        //
        string[] quotedWitNames = (from WorkItemConfig witConfig in workItemTypes
                                   select "'" + witConfig.Name + "'").ToArray();
        wiql = String.Format(wiql, String.Join(",", quotedWitNames), whereClause);

        return GetWorkItems(wiql, parameters);
    }

    public string FormatWorkItemEditUrl(int workItemId)
    {
        return _hyperlinkService.GetWorkItemEditorUrl(workItemId).AbsoluteUri;
    }

    private WorkItemInfo[] GetWorkItems(string wiql, Dictionary<string, object> parameters)
    {
        var treeQuery = new Query(_workItemStore, wiql, parameters);
        bool isTree = treeQuery.IsTreeQuery;

        //
        // First we want to get the list of work items and links from the query.
        //
        WorkItemLinkInfo[] links = treeQuery.RunLinkQuery();

        //
        // Build the list of work items for which we want to retrieve more information
        //
        int[] ids = (from WorkItemLinkInfo info in links
                     select info.TargetId).ToArray();

        //
        // Next we want to create a new query that will retrieve all the column values from the original query, for
        // each of the work item IDs returned by the original query.
        //
        var detailsWiql = new StringBuilder();
        detailsWiql.AppendLine("SELECT");
        bool first = true;

        foreach (FieldDefinition field in treeQuery.DisplayFieldList)
        {
            detailsWiql.Append("    ");
            if (!first)
                detailsWiql.Append(",");
            detailsWiql.AppendLine("[" + field.ReferenceName + "]");
            first = false;
        }
        detailsWiql.AppendLine("FROM WorkItems");

        //
        // Get the work item details
        //
        var flatQuery = new Query(_workItemStore, detailsWiql.ToString(), ids);
        return BuildTree(links, flatQuery.RunQuery());
    }

    private WorkItemInfo[] BuildTree(WorkItemLinkInfo[] links, WorkItemCollection details)
    {
        WorkItem[] wis = (from WorkItem wi in details
                          select wi).ToArray();

        var infos = new List<WorkItemInfo>();
        WorkItemInfo parent = null;

        for (int i = 0; i < links.Length; i++)
        {
            if (links[i].SourceId == 0)
            {
                parent = new WorkItemInfo(wis[i]);
                infos.Add(parent);
            }
            else
                parent.AddChild(wis[i]);
        }

        return infos.ToArray();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="wiql">
    ///		The WIQL that you pass to this method should include all the fields you want to retrieve from the children. For example:
    ///		
    ///        select	[System.Id],
    ///                 [Microsoft.VSTS.Scheduling.RemainingWork],
    ///                 [System.State]
    ///        from WorkItems 
    /// </param>
    /// <param name="parentId"></param>
    /// <returns></returns>
    private IEnumerable<WorkItem> GetChildren(string wiql, int parentId)
    {
        Dictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("parentId", parentId);

        //
        // Get the work item and all the children of this work item
        //
        string childWiql = @"
            select	[System.Id]
            from WorkItemLinks 
              where Source.[System.Id] = @parentId
                and [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'
        ";
        Query query = new Query(_workItemStore, childWiql, parameters);
        WorkItemLinkInfo[] links = query.RunLinkQuery();

        int[] ids = (from link in links
                     where link.SourceId != 0
                     select link.TargetId).ToArray();

        WorkItemCollection items = _workItemStore.Query(ids, wiql);

        return (from WorkItem item in items
                select item);
    }
}